import qualified Data.Set as Set

type Operand = Int
data Operation = Nop | Acc | Jmp deriving (Show)
data Instruction = Instruction Operation Operand deriving (Show)

type PC = Int
type RegisterValue = Int
type RunRecord = Set.Set PC
type State = (PC, RegisterValue, RunRecord)

parseOperation :: String -> Operation
parseOperation string = case string of
    "nop" -> Nop
    "acc" -> Acc
    "jmp" -> Jmp
    _ -> error "Unkown operation" 

parseInstruction :: String -> Instruction
parseInstruction string = 
    Instruction (parseOperation operationString) (sign * read operandString)
    where
        operationString = take 3 string
        sign = if (string !! 4) == '+' then 1 else -1
        operandString = drop 5 string

parseInput :: String -> [Instruction]
parseInput = map parseInstruction . lines

runInstruction :: Instruction -> State -> State
runInstruction (Instruction operation operand) (pc,regValue,record) = 
    case operation of 
        Nop -> (pc+1, regValue, newRecord)
        Acc -> (pc+1, regValue+operand, newRecord)
        Jmp -> (pc+operand, regValue, newRecord) 
    where
        newRecord = Set.insert pc record

run :: [Instruction] -> RegisterValue
run insts = doRun insts (0,0,Set.empty)
    where
        doRun :: [Instruction] -> State -> RegisterValue
        doRun insts state@(pc,regValue,record)
            | Set.member pc record = regValue
            | otherwise = 
                doRun insts newState
                where
                    inst = insts !! pc
                    newState = runInstruction inst state

main :: IO ()
main = do
    input <- readFile "./input.txt" 
    print . run . parseInput $ input